/*
 * @Author: xiaohao
 * @Date: 2025-11-03 09:08:19
 * @LastEditors: xiaohao
 * @LastEditTime: 2025-11-05 11:32:15
 * @FilePath: /Project/ConcurrentMemoryPool/src/CentralCache.cc
 * @Description:
 */

#include "ConcurrentMemoryPool/CentralCache.h"

// 获取一个非空的Span
Span *CentralCache::GetOneSpan(SpanList &spanList, size_t size)
{
    // 1. 遍历spanList, 找到当前的spanList中第一个非空的Span并返回
    Span *it = spanList.Begin();
    while (it != spanList.End())
    {
        if (it->_freeList != nullptr)
        {
            return it;
        }
        it = it->_next;
    }

    // 先把CentralCache的锁释放掉
    spanList._mutex.unlock();

    // 2. 如果没有找到非空的Span, 只能找PageCache, 申请一个新的Page, 并将其切成多个Span返回
    PageCache::GetPageCacheInstance()._pageMutex.lock();
    Span *span = PageCache::GetPageCacheInstance().NewSpan(SizeClass::NumMovePage(size));
    span->_isUsed = true;
    PageCache::GetPageCacheInstance()._pageMutex.unlock();

    // 3.通过页号计算页的起始地址和大块内存的大小(字节数)
    // 页号<< PAGE_SHIFT 得到页的起始地址
    char *start = (char *)(span->_pageId << PAGE_SHIFT);
    size_t bytes = span->_numPages << PAGE_SHIFT;
    void *end = start + bytes;

    // 4. 把大块内存切成自由链表接起来
    // (1) 先切一块下来去做头, 方便尾插
    span->_freeList = start;
    start += size;
    void *tail = span->_freeList;

    while (start + size <= end)
    {
        // 尾插法
        GetNextObj(tail) = start;
        tail = GetNextObj(tail);
        start += size;
    }

    // 5. 最后一个Span的尾指针指向nullptr
    spanList._mutex.lock();
    spanList.PushFront(span); // 将新的Span插入到SpanList的头部
    spanList._mutex.unlock();
    return span;
}

// 获取CentralCache单例实例
CentralCache &CentralCache::GetCentralInstance()
{
    static CentralCache instance;
    return instance;
}

// 从中心缓存获取一定数量的对象给ThreadCache
size_t CentralCache::FetchRangePbj(void *&start, void *&end, size_t batchNum, size_t size)
{
    size_t index = SizeClass::Index(size);
    _spanList[index]._mutex.lock();

    Span *span = GetOneSpan(_spanList[index], size);
    assert(span != nullptr && span->_freeList != nullptr);

    // 从span中获取batchNum个对象
    // 如果不够batchNum个对象, 则返回实际获取的数量actualNum
    start = span->_freeList;
    end = start;
    size_t i = 0;
    size_t actualNum = 1;
    while (i < batchNum - 1 && GetNextObj(end) != nullptr)
    {
        end = GetNextObj(end);
        ++i;
        ++actualNum;
    }
    span->_freeList = GetNextObj(end);
    GetNextObj(end) = nullptr;
    span->_useCount += actualNum;

    _spanList[index]._mutex.unlock();
    return actualNum;
}

// 将一定数量的对象释放到span跨度
void CentralCache::ReleaseListToSpans(void *start, size_t size)
{
    size_t index = SizeClass::Index(size);
    _spanList[index]._mutex.lock();

    while (start != nullptr)
    {
        void *next = GetNextObj(start);
        Span *span = PageCache::GetPageCacheInstance().MapObjectToSpan(start);
        GetNextObj(start) = span->_freeList;
        span->_freeList = start;

        --span->_useCount;
        // 说明span切分出去的所以小内存都回来了, 把这个span可以再回收给PageCache, PageCache可以尝试去进行前后页合并
        if (span->_useCount == 0)
        {
            _spanList[index].Erase(span);
            span->_freeList = span->_next = span->_prev = nullptr;

            _spanList[index]._mutex.unlock();

            // 释放到PageCache时, 使用互斥锁, 防止PageCache的线程同时释放同一个span
            PageCache::GetPageCacheInstance()._pageMutex.lock();
            PageCache::GetPageCacheInstance().ReleaseSpanToPageCache(span);
            PageCache::GetPageCacheInstance()._pageMutex.unlock();

            _spanList[index]._mutex.lock();
        }

        start = next;
    }

    _spanList[index]._mutex.unlock();
}